#ifndef _lru_cache_using_boost_
#define _lru_cache_using_boost_

#include <boost/bimap.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/function.hpp>
#include <pthread.h>
#include <cassert>
#include <map>
#include <boost/thread.hpp>

// Class providing fixed-size (by number of records)
// LRU-replacement cache of a function with signature
// V f(K).
// SET is expected to be one of boost::bimaps::set_of
// or boost::bimaps::unordered_set_of
namespace http {
namespace server3 {
using namespace std;
template <
        typename K,
        typename V
        >
class lru_cache_using_boost
{
public:

    typedef K key_type;
    typedef V value_type;
    typedef boost::function<value_type(const key_type&)> function_type;

    typedef boost::bimaps::bimap<
    boost::bimaps::set_of<key_type>,
    boost::bimaps::list_of<value_type>
    > container_type;

    pthread_mutex_t mutex_insert;
    const size_t _capacity;
    container_type _container;


    // Constuctor specifies the cached function and
    // the maximum number of records to be stored.
    // In the case there's one thread using this object
    lru_cache_using_boost(size_t c)
        :_capacity(c)
    {
        assert(_capacity!=0);
        pthread_mutex_init(&mutex_insert, NULL);
    }

    ~lru_cache_using_boost()
    {
        pthread_mutex_destroy(&mutex_insert);
    }


    // Obtain value of the cached function for k
    // input: k ==> key
    //        function_id ==> for getting new value in the case of missing value
    //                    ==> threads could get value in parallel
    bool operator()(const key_type& k, value_type& v) {

        // Attempt to find existing record
        // Attempt to find existing record
        const typename container_type::left_iterator it
          =_container.left.find(k);

        //std::cout <<"Getting data"<<std::endl;
        if (it==_container.left.end()) {
            return false;
        } else {
            // We do have it:
            // std::cout <<"\tWe do have it from function id = "<<function_id<<std::endl;
            // update the access record view.
            pthread_mutex_lock(&mutex_insert);
            _container.right.relocate(
                        _container.right.end(),
                        _container.project_right(it)
                        );
            pthread_mutex_unlock(&mutex_insert);
            // Return the retrieved value
            v = it->second;
            return true;
        }

    }

    // Obtain the cached keys, most recently used element
    // at head, least recently used at tail.
    // This method is provided purely to support testing.
    template <typename IT> void get_keys(IT dst) const {
        typename container_type::right_const_reverse_iterator src
                =_container.right.rbegin();
        while (src!=_container.right.rend()) {
            *dst++=(*src++).second;
        }
    }

    void insert(const key_type& k,const value_type& v) {

        pthread_mutex_lock(&mutex_insert);
        assert(_container.size()<= _capacity);

        // If necessary, make space
        if (_container.size()==_capacity) {
            // by purging the least-recently-used element
            _container.right.erase(_container.right.begin());
        }

        // Create a new record from the key and the value
        // bimap's list_view defaults to inserting this at
        // the list tail (considered most-recently-used).
        _container.insert(
                    typename container_type::value_type(k,v)
                    );
        pthread_mutex_unlock(&mutex_insert);
    }
};
}
}
#endif
